﻿using System;
using System.ComponentModel;
using System.Threading;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
namespace BulkFileManager
{
    public partial class Form1 : Form
    {
        public const string NODIRECTORYERROR = "Please select a directory to scan through.";
        public const string VERSION = "1.6";
        #region "Variables and Delegates" 
        /// <summary>
        /// Continue scanning for duplicates unless this is true
        /// </summary>
        private bool shouldstop = false;
        /// <summary>
        /// Is the program currently scanning for duplicates
        /// </summary>
        private bool isscanning = false;

        private bool stopshredding = false;
        /// <summary>
        /// Add to the error message box
        /// </summary>
        /// <param name="text">The item to add</param>
        delegate void TextMessageCallback(string text);
        /// <summary>
        /// Enable or disable the scan button
        /// </summary>
        /// <param name="enabled">"Enabled" value</param>
        delegate void ButtonEnableCallback(bool enabled);
        /// <summary>
        /// Clear list callback.
        /// </summary>
        delegate void ListBoxClearCallback();
        private int TrackbarValue = 0;
        // Building something in for potential crossplatform problems -- this variable will dictate directory separator
        public string DirectoryModifier = "";
        /// <summary>
        /// Deduplication thread
        /// </summary>      
        private Thread workDupes = null;
        private Thread workShred = null;
        /// <summary>
        /// Bulk mover thread
        /// </summary>
        private Thread workMove = null;

        /// <summary>
        /// Zero byte file/empty directory scan thread
        /// </summary>
        private Thread workZbscan = null;

        private Thread workStrip = null;
        private Thread workReplace = null;
        private Thread workPrefix = null;
        private Thread workSplitjoin = null;
        /// <summary>
        /// Always fully scan the following extensions.
        /// </summary>
        public static string[] AlwaysFullScan = new string[] { ".xls", ".doc", ".xlsx", ".docx", ".html", ".htm" };
        /// <summary>
        /// Directories to skip parsing.
        /// </summary>
        public static string[] DirectoriesToSkip = new string[] { "$Recycle.Bin", "Windows", "Program Files", "AppData", "Temp", "ProgramData","cygwin","Application Data" };
        /// <summary>
        /// Do not scan the following extensions.
        /// </summary>
        public static string[] DoNotScan = new String[] { ".sys" };
        // hash, dupecheck
        public Dictionary<string, DupeCheck> FirstRuns = new Dictionary<string, DupeCheck>();
        // filename,Dupecheck list
        public Dictionary<string, List<DupeCheck>> Dupes = new Dictionary<string, List<DupeCheck>>();
        // dupefilename,firstrunfilename
        public Dictionary<string, string> DupeLink = new Dictionary<string, string>();
        #endregion
        #region "Type definitions"
        public class DupeCheck
        {
            /// <summary>
            /// Timestamp of the dupecheck
            /// </summary>
            public DateTime DateTimestamp = DateTime.Now;
            /// <summary>
            /// File info related to the dupecheck item
            /// </summary>
            public FileInfo fInfo;
            /// <summary>
            /// Corresponds to radio button in deduplication options
            /// </summary>
            public enum ScanType
            {
                First2MB,
                Last2MB,
                BothFirstAndLast
            }
            /// <summary>
            /// Get the information about the file, and scan it based on the scan type.
            /// </summary>
            /// <param name="FiletoCheck">The FileInfo of the file you want to hash out.</param>
            /// <param name="st">The scan type ot use per the enum aobve</param>
            public DupeCheck(FileInfo FiletoCheck,ScanType st)
            {
                // Assign the file passed in to the object.
                this.fInfo = FiletoCheck;
                // Load the reader from the file.
                StreamReader reader = new StreamReader(FiletoCheck.FullName);
                // Used to store the contents of the file.
                string contents = "";
                // If the reader is under the 2MB mark and not designated as always full scan, then we need to limit the scan accordingly.
                if (reader.BaseStream.Length > 2048000 && !AlwaysFullScan.Contains(FiletoCheck.Extension))
                {
                    // Set up our buffer to run through, keeps RAM usage down.
                    char[] buff = new char[65000];
                    // holds return value for reader.
                    int i = 0;
                    // bytecount
                    int track = 0;
                    // If we're set to scan the first two mb, do so and append it to the contents.
                    if (st == ScanType.First2MB || st == ScanType.BothFirstAndLast)
                    {
                        while (track < 2048000)
                        {
                            // Read the next 65000 characters into the buff variable, i holds the position.
                            i = reader.Read(buff, 0, 65000);
                            // Adjust track accordingly.
                            track += 65000;
                            // Append to contents
                            contents += new String(buff);
                        }
                    }
                    // reset variables for last scan
                    track = 0;
                    i = 0;
                    // Repeat process for last 2mb if needed
                    if (st == ScanType.Last2MB || st == ScanType.BothFirstAndLast)
                    {
                        // Reload reader
                        reader.Close();
                        reader.Dispose();
                        reader = new StreamReader(FiletoCheck.FullName);
                        // Move position to the end of the file (to prevent having to read the entire file to get the last 2mb)
                        reader.BaseStream.Position = reader.BaseStream.Length - 2048000;
                        // Read to the end of the file.
                        string getContents = reader.ReadToEnd();
                        // Safety check to prevent out of bounds exceptions that occurred in testing
                        if (getContents.Length >= 2048000)
                            contents += getContents.Substring(getContents.Length - 2048000);
                        else
                            contents += getContents;
                        // Dispose of the reader
                        reader.Close();
                        reader.Dispose();
                    }
                }
                else
                {
                    // Read the entire file if it is designated in the extension list
                    contents = reader.ReadToEnd();
                }
                // Hash the contents out
                this.Hash = GetSHA1(contents, Encoding.Default);
            }
            /// <summary>
            /// Hash function
            /// </summary>
            /// <param name="filecontents">String to hash</param>
            /// <param name="encoding">Encoding to use to hash</param>
            /// <returns>SHA1 Hash of the string</returns>
            public static string GetSHA1(string filecontents, Encoding encoding)
            {
                byte[] buf = encoding.GetBytes(filecontents);
                SHA1CryptoServiceProvider SHA1Hasher = new SHA1CryptoServiceProvider();
                string hash = BitConverter.ToString(SHA1Hasher.ComputeHash(buf)).Replace("-", "");

                return hash;
            }
            public string Hash;
        }
        #endregion
        #region "Form Constructor"
        public Form1()
        {
            InitializeComponent();
            // Populate the variables for 
            tbx_DeepScan.Text = String.Join("|", AlwaysFullScan);
            tbx_DoNotScanDirs.Text = String.Join("|", DirectoriesToSkip);
            tbx_DoNotScanExts.Text = String.Join("|", DoNotScan);
            if (Application.ExecutablePath.Contains("\\"))
                this.DirectoryModifier = "\\";
            else if (Application.ExecutablePath.Contains("/"))
                this.DirectoryModifier = "/";
            else
                this.DirectoryModifier = "\\";
        }
        #endregion
        #region "Handle Prefix"
        #region "Directory Selection"
        /// <summary>
        /// Standard "Select Folder" button click, will not comment these again.
        /// </summary>
        /// <param name="sender">Event parameters</param>
        /// <param name="e">Event parameters</param>
        private void button2_Click(object sender, EventArgs e)
        {
            // Check if a directory is in the text box
            string prefixDir = tbx_PrefixFolder.Text;
            // If so, and it exists, point the file browser dialog at it.
            if (Directory.Exists(prefixDir))
                folderBrowserDialog1.SelectedPath = tbx_PrefixFolder.Text;
            // Show dialog box and check result. If ok, populate path from it.
            if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
            {
                tbx_PrefixFolder.Text = folderBrowserDialog1.SelectedPath;
            }
        }
        #endregion
        #region "Prefix Thread function"
        /// <summary>
        /// The function that is used to thread the Prefix/Suffix work.
        /// </summary>
        private void DoPrefixWork()
        {
            // Call the cross-thread safe method.
            this.SetStatusBar("Running prefix.");
            // Disable the prefix button during scan (another cross-thread safe method)
            this.WorkPrefixButton(false);
            // Run the prefix command in this thread.
            this.PrefixParse(new DirectoryInfo(tbx_PrefixFolder.Text));
            // When done, reenable the button.
            this.WorkPrefixButton(true);
            // Build out status message.
            string filedir = "files";
            if (cbx_Prefix_Directories.Checked)
                filedir += " and directory name(s)";
            string prefixed = "Processed '" + tbx_Prefix_To_Add.Text + "' to " + filedir + " inside the directory " + tbx_PrefixFolder.Text;
            if (cbx_Recurse_Prefix.Checked)
                prefixed += " recursively.";
            else
                prefixed += ".";
            // Set status message.
            this.SetStatusBar(prefixed);
        }//private void DoPrefixWork()
        #endregion
        #region "Set Prefix/Suffix button enabled state"
        /// <summary>
        /// Basic recursive invocation method.
        /// </summary>
        /// <param name="enabled">Parameter to pass to prefix button's enabled property</param>
        private void WorkPrefixButton(bool enabled)
        {            
            // Check if invoke is necessary to prevent infinite recursion
            if (button1.InvokeRequired)
            {
                // Set up delegate method
                ButtonEnableCallback d = new ButtonEnableCallback(WorkPrefixButton);
                // Invoke delegate method with current parameter
                button1.Invoke(d, new object[] { enabled });
            }
            else
            {
                // If we're here, we're at the proper thread. Enable/disable the button.
                button1.Enabled = enabled;
            }
        }
        #endregion
        #region "Start prefixing button"
        /// <summary>
        /// Prefix "Start scan" button.
        /// </summary>
        /// <param name="sender">Event parameters</param>
        /// <param name="e">Event parameters</param>
        private void button1_Click(object sender, EventArgs e)
        {           
            // Check to see if the folder text is empty
            if (tbx_PrefixFolder.Text != "")
            {
                // Assign function to the thread. Function will control status bar, button, checking directory existence, etc.
                workPrefix = new System.Threading.Thread(new System.Threading.ThreadStart(DoPrefixWork));
                // start thread
                workPrefix.Start();
            }
            else
            {
                // Show no directory warning
                MessageBox.Show(Form1.NODIRECTORYERROR);
            }
        }
        #endregion
        #region "Prefix Function"
        /// <summary>
        /// Function to recurse through and prefix files and directories.
        /// </summary>
        /// <param name="dir">The directory info to scan through</param>
        public void PrefixParse(DirectoryInfo dir)
        {
            // First, prefix each file in the directory.
            foreach (FileInfo fInfo in dir.GetFiles())
            {
                // Set up try/catch for IO Errors
                try
                {
                    // Get the filename. This is for suffixing.
                    string filename = fInfo.Name.Substring(0, fInfo.Name.IndexOf(fInfo.Extension));
                    // Check if we're prefixing or suffixing, then apply the proper string manipulation.
                    if (radio_Prefix.Checked)
                        System.IO.File.Move(fInfo.FullName, fInfo.FullName.Replace(fInfo.Name, tbx_Prefix_To_Add.Text + fInfo.Name));
                    else if (radio_Suffix.Checked)
                        System.IO.File.Move(fInfo.FullName, fInfo.FullName.Replace(fInfo.Name, filename + tbx_Prefix_To_Add.Text + fInfo.Extension));
                }
                catch (Exception ex)
                {
                    // Add the exception to the error log.
                    this.ErrorMessageAdd("Unable to move file " + fInfo.FullName + ". Reason: " + ex.Message);
                }
            }
            // If we're told to recurse directories..
            if (cbx_Recurse_Prefix.Checked)
            {
                // Iterate through the subdirectories and run them through this function.
                foreach (DirectoryInfo dInfo in dir.GetDirectories())
                    this.PrefixParse(dInfo);
            }
            // If we're supposed to prefix/suffix the directory, as well..
            if (cbx_Prefix_Directories.Checked)
            {
                // I/O Exceptions
                try
                {
                    // Check if we're prefixing or suffixing, then apply it.
                    if (radio_Prefix.Checked)
                        System.IO.Directory.Move(dir.FullName, dir.FullName.Replace(dir.Name, tbx_Prefix_To_Add.Text + dir.Name));
                    else if (radio_Suffix.Checked)
                        System.IO.Directory.Move(dir.FullName, dir.FullName.Replace(dir.Name, dir.Name + tbx_Prefix_To_Add.Text));
                }   
                catch (Exception ex)
                {
                    // Add the exception message to error log
                    this.ErrorMessageAdd("Unable to move directory " + dir.FullName + ". Reason: " + ex.Message);
                }
            }
        }
        #endregion
        #endregion
        #region "Handle Strip"
        #region "Strip function"
        /// <summary>
        /// Function to handle scanning through directories and stripping text -- called by threaded function
        /// </summary>
        /// <param name="dir">Directory to parse through</param>
        public void StripParse(DirectoryInfo dir)
        {
            // Iterate through the FileInfos
            foreach (FileInfo fInfo in dir.GetFiles())
            {
                // IO Exceptions
                try
                {
                    // If we're told to use regex, then run it through Regex.Replace() instead of String.Replace()
                    if(!cbx_Strip_Regex.Checked)
                        System.IO.File.Move(fInfo.FullName, fInfo.FullName.Replace(fInfo.Name, fInfo.Name.Replace(tbx_Strip.Text, "")));
                    else
                        System.IO.File.Move(fInfo.FullName, fInfo.FullName.Replace(fInfo.Name, System.Text.RegularExpressions.Regex.Replace(fInfo.Name, tbx_Strip.Text, "")));
                }
                catch (Exception ex)
                {
                    // Add to error log
                    this.ErrorMessageAdd("Unable to move file " + fInfo.FullName + ". Reason: " + ex.Message);
                }

            }            
            // Check if the recurse directories checkbox is checked before attempting to strip from this directory name
            if (cbx_Recursive_Strip.Checked)
            {
                // Iterate through directories
                foreach (DirectoryInfo dInfo in dir.GetDirectories())
                {
                    //  Parse subdirectories
                    this.StripParse(dInfo);
                }
            }
            // Strip from directory checkbox
            if (cbx_Strip_Directories.Checked)
            {
                // IO Exception
                try
                {
                    // Use regex?
                    if(!cbx_Strip_Regex.Checked)
                        System.IO.Directory.Move(dir.FullName, dir.FullName.Replace(dir.Name, dir.Name.Replace(tbx_Strip.Text, "")));
                    else
                        System.IO.Directory.Move(dir.FullName, dir.FullName.Replace(dir.Name, System.Text.RegularExpressions.Regex.Replace(dir.Name, tbx_Strip.Text, "")));                    
                }
                catch (Exception ex)
                {
                    // Error log
                    this.ErrorMessageAdd("Unable to move directory " + dir.FullName + ". Reason: " + ex.Message);
                }
            }
        }
        #endregion
        #region "Select folder for Strip function"
        /// <summary>
        /// "Select folder" button for strip text (to start strip function)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button4_Click(object sender, EventArgs e)
        {
            string stripDir = tbx_Strip_Dir.Text;
            if (Directory.Exists(stripDir))
                folderBrowserDialog1.SelectedPath = stripDir;
            if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
            {
                tbx_Strip_Dir.Text = folderBrowserDialog1.SelectedPath;                
            }            
        }
        #endregion
        #region "button click to run strip function"
        /// <summary>
        /// The function to call from the thread.
        /// </summary>
        private void DoStripWork()
        {
            // Set the statusbar.
            this.SetStatusBar("Filename text strip in progress.");
            // Disable button during scan.
            this.WorkStripButton(false);
            // Scan through.
            this.StripParse(new DirectoryInfo(tbx_Strip_Dir.Text));
            // Enable button again
            this.WorkStripButton(true);
            // Make status bar message.
            string filedir = "files";
            if (cbx_Strip_Directories.Checked)
                filedir += " and directory name(s)";
            string status = "Stripped '" + tbx_Strip.Text + "' from " + filedir + " inside the directory " + tbx_Strip_Dir.Text;
            if (cbx_Recursive_Strip.Checked)
                status += " recursively.";
            else
                status += ".";
            // Set status bar.
            this.SetStatusBar(status);
        }
        /// <summary>
        /// Function run when the button "Strip Text" is clicked.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button3_Click(object sender, EventArgs e)
        {
            // Check if dir is not empty
            if (tbx_Strip_Dir.Text != "")
            {
                // Set up the thread, it does the error checking for us.
                workStrip = new System.Threading.Thread(new System.Threading.ThreadStart(DoStripWork));
                // Start thread.
                workStrip.Start();
            }
            else
            {
                // Show no directory error.
                MessageBox.Show(Form1.NODIRECTORYERROR);
            }
        }
        #endregion
        #endregion
        #region "String Replacement"
        /// <summary>
        /// Replace text select folder button
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button5_Click(object sender, EventArgs e)
        {
            string replaceDir = tbx_Replace_Dir.Text;
            if (Directory.Exists(replaceDir))
                folderBrowserDialog1.SelectedPath = replaceDir;
            if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
            {
                tbx_Replace_Dir.Text = folderBrowserDialog1.SelectedPath;                
            }            
        }
        /// <summary>
        /// Threadsafe invocation of the replace button
        /// </summary>
        /// <param name="enabled">Bool to set for Enabled property</param>
        private void WorkReplaceButton(bool enabled)
        {
            if (this.btn_Replace.InvokeRequired)
            {
                ButtonEnableCallback d = new ButtonEnableCallback(WorkReplaceButton);
                this.btn_Replace.Invoke(d, new object[] { enabled });
            }
            else
            {
                this.btn_Replace.Enabled = enabled;
            }
        }
        /// <summary>
        /// The threaded function to call for Replace Text, refer to the earlier functions for detailed comments.
        /// </summary>
        private void WorkReplace()
        {
            this.SetStatusBar("Replacing text in file names.");
            this.WorkReplaceButton(false);
            this.ReplaceParse(new DirectoryInfo(tbx_Replace_Dir.Text));
            this.WorkReplaceButton(true);
            string filedir = "files";
            if (cbx_Replace_Dirname.Checked)
                filedir += " and directory name(s)";
            string status = "Replaced '" + tbx_Replace.Text + "' with '" + tbx_Replace_With.Text + "' in " + filedir + " inside the directory " + tbx_Replace_Dir.Text;
            if (cbx_Recurse_Replace.Checked)
                status += " recursively.";
            else
                status += ".";
            this.SetStatusBar(status);
        }
        /// <summary>
        /// Replace text button click
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Replace_Click(object sender, EventArgs e)
        {
            if (this.cbx_Replace_Text_In_Filename.Checked || this.cbx_Replace_Dirname.Checked || this.cbx_Replace_Data_Within_File.Checked)
            {
                if (this.tbx_Replace_Dir.Text != "" && this.tbx_Replace_With.Text != "")
                {
                    this.workReplace = new Thread(new System.Threading.ThreadStart(WorkReplace));
                    this.workReplace.Start();
                }
                else
                {
                    string message = "";
                    if (this.tbx_Replace_Dir.Text != "")
                    {
                        message = "You need to specify a directory to operate within";
                        if (tbx_Replace.Text != "" || tbx_Replace_With.Text != "")
                            message += " and both replacement values.";
                        else
                            message += ".";
                    }
                    else
                    {
                        message += "Please specify both text values (Both text to replace and the text to replace it with).";
                    }
                    MessageBox.Show(message);
                }
            }
            else
            {
                MessageBox.Show("No replacement option checked. Please select one of the following: 'Replace text in directories', 'Replace text in file name', 'Replace data within file's contents'");
            }
        }
        /// <summary>
        ///  Handle replacements
        /// </summary>
        /// <param name="dir">Directory to scan</param>
        public void ReplaceParse(DirectoryInfo dir)
        {
            if (cbx_Replace_Data_Within_File.Checked)
            {
                long getScanSize = ((long)numericDontReplaceOver.Value * (1024 * 1024));
                foreach (FileInfo fInfo in dir.GetFiles())
                {
                    FileStream fStream = new FileStream(fInfo.FullName,FileMode.Open);
                    StreamReader sRead = new StreamReader(fStream);
                    this.SetStatusBar("Replacing text within file " + fInfo.Name);
                    string readtoEnd = sRead.ReadToEnd();
                    sRead.Close();
                    fStream.Close();
                    if (cbx_RegexReplace.Checked)
                    {
                        readtoEnd = System.Text.RegularExpressions.Regex.Replace(readtoEnd, tbx_Replace.Text, tbx_Replace_With.Text);
                    }
                    else
                    {
                        readtoEnd = readtoEnd.Replace(tbx_Replace.Text, tbx_Replace_With.Text); 
                    }
                    fStream = new FileStream(fInfo.FullName, FileMode.Truncate);
                    StreamWriter sWrite = new StreamWriter(fStream);
                    sWrite.Write(readtoEnd);
                    sWrite.Close();
                    fStream.Close();
                    this.SetStatusBar("Text replaced.");
                }
            }
            if (cbx_Replace_Text_In_Filename.Checked)
            {
                // Get every FileInfo in the dir
                foreach (FileInfo fInfo in dir.GetFiles())
                {
                    this.SetStatusBar("Replacing text in filename for " + fInfo.Name); 
                    // IO Exceptions
                    try
                    {
                        // If Regex is supposed to be used, use Regex.Replace() instead of String.Replace()
                        if (!this.cbx_RegexReplace.Checked)
                            System.IO.File.Move(fInfo.FullName, fInfo.FullName.Replace(fInfo.Name, fInfo.Name.Replace(tbx_Replace.Text, tbx_Replace_With.Text)));
                        else
                            System.IO.File.Move(fInfo.FullName, fInfo.FullName.Replace(fInfo.Name, System.Text.RegularExpressions.Regex.Replace(fInfo.Name, tbx_Replace.Text, tbx_Replace_With.Text)));
                    }
                    catch (Exception ex)
                    {
                        // Add exception message to error log
                        this.ErrorMessageAdd("Unable to move file " + fInfo.FullName + ". Reason: " + ex.Message);
                    }
                    this.SetStatusBar("Text replaced.");
                }
            }
            // Check if we need to recurse
            if (cbx_Recurse_Replace.Checked)
            {                
                // Recurse
                foreach (DirectoryInfo dInfo in dir.GetDirectories())
                {
                    this.ReplaceParse(dInfo);
                }
            }
            // Replace within the dirname?
            if (cbx_Replace_Dirname.Checked)
            {
                try
                {
                    // Check for regex
                    if(cbx_RegexReplace.Checked)
                        System.IO.Directory.Move(dir.FullName, dir.FullName.Replace(dir.Name, dir.Name.Replace(tbx_Replace.Text, tbx_Replace_With.Text)));
                    else
                        System.IO.Directory.Move(dir.FullName, dir.FullName.Replace(dir.Name, System.Text.RegularExpressions.Regex.Replace(dir.Name, tbx_Replace.Text, tbx_Replace_With.Text)));
                }
                catch (Exception ex)
                {
                    this.ErrorMessageAdd("Unable to move directory " + dir.FullName + ". Reason: " + ex.Message);
                }
            }
        }

        #endregion
        #region "Menu Items"
        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }
        #endregion
        #region "Handle Dedupe locator"
        #region "Select DeDupe Folder"
        /// <summary>
        /// Select folder button click for dedupe
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button6_Click(object sender, EventArgs e)
        {
            string dedupeDir = tbx_DedupeFolder.Text;
            if (Directory.Exists(dedupeDir))
                folderBrowserDialog1.SelectedPath = dedupeDir;
            if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
            {
                tbx_DedupeFolder.Text = folderBrowserDialog1.SelectedPath;
            }                           
        }
        #endregion
        #region "Dedupe function"
        /// <summary>
        /// Handle deduplication of the files
        /// </summary>
        /// <param name="dir">Directory to scan</param>
        public void Dedupe(DirectoryInfo dir)
        {
            // Exterior IO Exceptions
            try
            {
                // Iterate FileInfos
                foreach (FileInfo fInfo in dir.GetFiles())
                {
                    // Check if we're NOT supposed to scan
                    if (!DoNotScan.Contains(fInfo.Extension))
                    {
                        // Interior IO Exception (prevents abortion of thread in case of inability to access a root level file/directory)
                        try
                        {
                            // Check if the file is above 0 bytes
                            if (fInfo.Length != 0)
                            {
                                // Make sure the stop button has not been pressed before continuing.
                                if (shouldstop != true)
                                {
                                    // Set up status bar for the file
                                    this.SetStatusBar("Dedupe scanning directory: " + dir.FullName + ", file: " + fInfo.Name);
                                    // Determine the scantype based on the radio buttons.
                                    DupeCheck.ScanType sType = new DupeCheck.ScanType();
                                    if(radio_scan_First2mb.Checked)
                                        sType = DupeCheck.ScanType.First2MB;
                                    if (radio_Scan_FirstAndLast2mb.Checked)
                                        sType = DupeCheck.ScanType.BothFirstAndLast;
                                    if (radio_Scan_Last2mb.Checked)
                                        sType = DupeCheck.ScanType.Last2MB;
                                    // Set up dupecheck object
                                    DupeCheck dCheck = new DupeCheck(fInfo,sType);
                                    // See if the FirstRuns (Lefthand listbox files) contains the hash of this file
                                    if (FirstRuns.Keys.Contains(dCheck.Hash))
                                    {
                                        //See if it is already in the list, if not, add it.
                                        if (!lbx_FileCollisions.Items.Contains(FirstRuns[dCheck.Hash].fInfo.FullName))
                                            this.AddFirstRun(FirstRuns[dCheck.Hash].fInfo.FullName);
                                        // See if the dupes (the lists that populate the righthand listbox) list has the value for this file, if not, create it
                                        if (!Dupes.Keys.Contains(FirstRuns[dCheck.Hash].fInfo.FullName))
                                            Dupes[FirstRuns[dCheck.Hash].fInfo.FullName] = new List<DupeCheck>();
                                        // Add this instance to the lists that populate the righthand listbox
                                        Dupes[FirstRuns[dCheck.Hash].fInfo.FullName].Add(dCheck);
                                        // Add the link we use for the context menu deletions
                                        DupeLink[dCheck.fInfo.FullName] = FirstRuns[dCheck.Hash].fInfo.FullName;
                                    }
                                    else
                                    {
                                        // Add the file to the list of hashes we've checked, it was not a duplicate.
                                        FirstRuns.Add(dCheck.Hash, dCheck);
                                    }
                                    // Check slider value and sleep the thread as needed.
                                    if (TrackbarValue == 0)
                                    {
                                        Thread.Sleep(750);
                                    }
                                    else if (TrackbarValue == 1)
                                    {
                                        Thread.Sleep(375);
                                    } 
                                }
                                else
                                {
                                    // Skip until we're through, exit button was clicked.
                                    this.SetStatusBar("Exiting scan.");
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            // Add IO Exception to log
                            this.ErrorMessageAdd("Unable to check file " + fInfo.FullName + " for following reason: " + ex.Message);
                        }
                       
                    }
                    else
                    {
                        // Add DoNotScan notification to error log (not technically an error)
                        this.ErrorMessageAdd("Scan skipped on file " + fInfo.FullName + " because extension is in NoScan list.");
                    }
                }
                // Check if we are still ok to continue
                if (shouldstop != true)
                {
                    // Check subdirectories
                    foreach (DirectoryInfo subDir in dir.GetDirectories())
                    {
                        // Check if the subdirectory is in the skip directories list or not, we'll automatically recurse directories otherwise.
                        if (!DirectoriesToSkip.Contains(subDir.Name))
                            this.Dedupe(subDir);
                    }
                    this.SetStatusBar("Finished dedupe scan for directory: " + dir.FullName);
                }
            }
            catch (Exception ex)
            {
                // Likely a permissions error.                
                this.ErrorMessageAdd("Error encountered: " + ex.Message);
            }
        }
        #endregion
        #region "Left-hand list box selected index change."
        private void lbx_FileCollisions_SelectedIndexChanged(object sender, EventArgs e)
        {
            // Someone selected a file in the lefthand dedupe listbox, clear the RIGHTHAND list.
            lbx_FilesCollided.Items.Clear();
            // Check if we have only one item selected, otherwise the righthand listbox should remain empty for now.
            if (lbx_FileCollisions.SelectedItems.Count == 1)
            {
                // Selected file texts
                lbl_Collision.Text = "Selected \"Collision\" file: " + lbx_FileCollisions.SelectedItem + ".";
                lbl_Collided.Text = "Selected \"Collided\" file: N/A.";
                // Adjust context menus
                openFileLocationToolStripMenuItem1.Enabled = true;
                deleteAllDupesToolStripMenuItem.Enabled = true;
                // Get the files connected to this one.
                string fileCheck = lbx_FileCollisions.SelectedItem.ToString().Replace(@"\\", @"\");
                if (Dupes.Keys.Contains(fileCheck))
                {
                    // Iterate through the dupes in the list
                    foreach (DupeCheck dCheck in Dupes[fileCheck])
                    {
                        // Add them to the righthand listbox
                        lbx_FilesCollided.Items.Add(dCheck.fInfo.FullName);
                    }
                }
                else
                {
                    this.SetStatusBar("Unable to find this item in the dupe list.");
                }
            }
            else
            {
                // Adjust text and context menu for multiple/no file selections
                if (lbx_FileCollisions.SelectedItems.Count > 1)
                {
                    lbl_Collision.Text = "Selected \"Collision\" file: Multiple.";
                    lbl_Collided.Text = "Multiple files selected, no collisions displayed.";
                    openFileLocationToolStripMenuItem.Enabled = false;
                    deleteAllDupesToolStripMenuItem.Enabled = true;                    
                }
                else
                {
                    openFileLocationToolStripMenuItem1.Enabled = false;
                    deleteAllDupesToolStripMenuItem.Enabled = false;
                }
            }
        }
        #endregion
        #region "Delete selected item right-hand context menu function"
        private void deleteToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Check if the selected item is null
            if (lbx_FilesCollided.SelectedItem != null)
            {
                // Check if the string is not null
                if (lbx_FilesCollided.SelectedItem.ToString() != "")
                {
                    // Check for existence of the file.
                    if (File.Exists(lbx_FilesCollided.SelectedItem.ToString()))
                    {
                        // IO Exception
                        try
                        {
                            // Attempt to delete
                            string filetoDelete = lbx_FilesCollided.SelectedItem.ToString();
                            File.Delete(filetoDelete);                            
                            this.SetStatusBar("Deleted file " + filetoDelete);
                            lbx_FilesCollided.Items.Remove(filetoDelete);                            
                            if(lbx_FilesCollided.Items.Count == 0)
                            {
                                //string hashretrieval = DupeLink[filetoDelete][0].Hash;
                                //string fullnameget = FirstRuns[hashretrieval].fInfo.FullName;
                                string CollisiontoResolve = DupeLink[filetoDelete];
                                lbx_FileCollisions.Items.Remove(CollisiontoResolve);
                            }
                        }
                        catch (Exception ex)
                        {
                            this.ErrorMessageAdd("Unable to delete file. Reason: " + ex.Message);
                        }
                    }
                }
            }
        }
        #endregion 
        #region "Scan Button click"
        private void btn_ScanDupes_Click(object sender, EventArgs e)
        {          
            try
            {
                // Starting new scan reset variables
                shouldstop = false;
                DupeLink.Clear();
                Dupes.Clear();
                FirstRuns.Clear();
                // Disable the slider
                trackBar1.Enabled = false;
                // get the CPU priority slider value
                TrackbarValue = trackBar1.Value;    
                // Set up thread.
                workDupes = new System.Threading.Thread(new System.Threading.ThreadStart(ThreadDupes));
                // Assign priority.
                switch (trackBar1.Value)
                {
                    case 0:
                        workDupes.Priority = ThreadPriority.Lowest;
                        break;
                    case 1:
                        workDupes.Priority = ThreadPriority.BelowNormal;
                        break;
                    case 2:
                        workDupes.Priority = ThreadPriority.Normal;
                        break;
                    default:
                        workDupes.Priority = ThreadPriority.Normal;
                        break;
                }                
                // Start thread.
                workDupes.Start();                
                // Enable the cancel scan button.
                btn_CancelScan.Enabled = true;
                // Get rid of any old search data.
                lbx_FileCollisions.Items.Clear();
                lbx_FilesCollided.Items.Clear();
                lbl_Collided.Text = "Selected \"Collided\" file: N/A.";
                lbl_Collision.Text = "Selected \"Collision\" file: N/A.";
            }
            catch (Exception ex)
            {
                this.ErrorMessageAdd("Error in dedupe scan: " + ex.Message);
            }
        }
        #endregion
        #region "Deduplication threaded function"
        // Thread function for dupes
        private void ThreadDupes()
        {
            if (tbx_DedupeFolder.Text != "")
            {
                // Get the configuration options in case the users changed the defaults
                AlwaysFullScan = tbx_DeepScan.Text.Split('|');
                DirectoriesToSkip = tbx_DoNotScanDirs.Text.Split('|');
                DoNotScan = tbx_DoNotScanExts.Text.Split('|');
                //  set status bar
                this.SetStatusBar("Scanning for duplicates..");
                // change control states due to scan
                this.CancelButtonEnabled(true);
                this.PriorityControlEnabled(false);
                // set up scanning variable
                this.isscanning = true;
                // start dedupe scan
                this.Dedupe(new DirectoryInfo(tbx_DedupeFolder.Text));
                //Enable controls if scan is over
                if(this.btn_CancelScan.Enabled == true)
                    this.CancelButtonEnabled(false);
                this.PriorityControlEnabled(true);
                // change scanning variable
                this.isscanning = false;
            }
            else
            {
                MessageBox.Show(Form1.NODIRECTORYERROR);
            }
        }
        #endregion
        #region "Open File Location right-hand context menu"
        private void openFileLocationToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Open file's location in explorer
            if (lbx_FilesCollided.SelectedItem != null)
            {
                string Directoryname = lbx_FilesCollided.SelectedItem.ToString().Substring(0, lbx_FilesCollided.SelectedItem.ToString().LastIndexOf("\\"));
                if (Directory.Exists(Directoryname))
                    System.Diagnostics.Process.Start("explorer.exe", Directoryname);
                else
                    this.ErrorMessageAdd("Unable to open folder location.");
            }
        }
        #endregion
        #region "Invoke Methods"
        private void WorkStripButton(bool enabled)
        {
            if (this.button3.InvokeRequired)
            {
                ButtonEnableCallback d = new ButtonEnableCallback(WorkStripButton);
                button3.Invoke(d, new object[] { enabled });
            }
            else
            {
                button3.Enabled = enabled;
            }
        }
        private void AddDupe(string text)
        {
            if (this.lbx_FilesCollided.InvokeRequired)
            {
                TextMessageCallback d = new TextMessageCallback(AddDupe);
                this.Invoke(d, new object[] { text });
            }
            else
            {
                this.lbx_FilesCollided.Items.Add(text);
            }
        }
        private void AddFirstRun(string text)
        {
            if (this.lbx_FileCollisions.InvokeRequired)
            {
                TextMessageCallback d = new TextMessageCallback(AddFirstRun);
                this.Invoke(d, new object[] { text });
            }
            else
            {
                this.lbx_FileCollisions.Items.Add(text);
            }
        }        
        private void SetStatusBar(string text)
        {
            if (this.statusStrip1.InvokeRequired)
            {
                TextMessageCallback d = new TextMessageCallback(SetStatusBar);
                statusStrip1.Invoke(d, new object[] { text });
            }
            else
            {
                string writeout = "";
                if (text.Length > 150)
                {
                    writeout = text.Substring(0, text.Substring(0, 150).LastIndexOf(" ")) + " ...";
                }
                else
                    writeout = text;
                this.toolStripStatusLabel1.Text = writeout;
            }
        }
        private void CancelButtonEnabled(bool enabled)
        {
            if (this.btn_CancelScan.InvokeRequired)
            {
                ButtonEnableCallback d = new ButtonEnableCallback(CancelButtonEnabled);
                btn_CancelScan.Invoke(d, new object[] { enabled });
            }
            else
            {
                this.btn_CancelScan.Enabled = enabled;
            }
        }
        private void PriorityControlEnabled(bool enabled)
        {
            if (this.trackBar1.InvokeRequired)
            {
                ButtonEnableCallback d = new ButtonEnableCallback(PriorityControlEnabled);
                trackBar1.Invoke(d, new object[] { enabled });
            }
            else
            {
                this.trackBar1.Enabled = enabled;
            }
        }
        #endregion
        #region "Right-hand listbox selected index changed"
        // Handle righthand listbox being clicked on
        private void lbx_FilesCollided_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (lbx_FilesCollided.SelectedItem != null)
            {
                lbl_Collided.Text = "Selected \"Collided\" file: " + lbx_FilesCollided.SelectedItem + ".";
                contextMenuStrip1.Enabled = true;
            }
            else
                contextMenuStrip1.Enabled = false;
        }
        #endregion
        #region "Right-hand Context Menu opening"
        // Set menu option states
        private void contextMenuStrip1_Opening(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (lbx_FilesCollided.SelectedItem == null)
            {
                openFileLocationToolStripMenuItem.Enabled = false;
                deleteToolStripMenuItem.Enabled = false;
            }
            else
            {
                openFileLocationToolStripMenuItem.Enabled = true;
                deleteToolStripMenuItem.Enabled = true;
            }
        }
        #endregion
        #region "Cancel scan button"
        private void btn_CancelScan_Click(object sender, EventArgs e)
        {
            // Abort the scan
            this.shouldstop = true;
            btn_CancelScan.Enabled = false;
        }
        #endregion
        #region "Open file location left-pane context menu"
        private void openFileLocationToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            // Open selected file location in explorer
            if (lbx_FilesCollided.SelectedItem != null)
            {
                string Directoryname = "";
                if(lbx_FileCollisions.SelectedItem.ToString().IndexOf("\\") > 0)
                    Directoryname = lbx_FileCollisions.SelectedItem.ToString().Substring(0, lbx_FileCollisions.SelectedItem.ToString().LastIndexOf("\\"));
                else if(lbx_FileCollisions.SelectedItem.ToString().IndexOf("/") > 0)
                    Directoryname = lbx_FileCollisions.SelectedItem.ToString().Substring(0, lbx_FileCollisions.SelectedItem.ToString().LastIndexOf("/"));
                if (Directory.Exists(Directoryname))
                    System.Diagnostics.Process.Start("explorer.exe", Directoryname);
                else
                    this.ErrorMessageAdd("Unable to open folder location.");
            }
        }
        #endregion
        #region "About window"
        private void aboutBulkFileManagerToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MessageBox.Show("Bulk File Manager" + Form1.VERSION + " (2010) MIT License " + "\r\n" + "Updates at http://bulkfilemanager.codeplex.com/" + "\r\n" + "Contact @ bulkfilemanager@gmail.com" + "\r\n" + "C# .NET 3.5 Open-source File Deduplication/File name management solution.");
        }
        #endregion
        #region "Delete All Selected Dupes Context Menu Buttons"
        private void deleteAllDupesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Check if we have an item to delete
            if (lbx_FileCollisions.SelectedItems.Count == 1)
            {
                if (MessageBox.Show("Do you really want to delete dupes for the selected item?", "Confirm Deletion", MessageBoxButtons.YesNo) == DialogResult.Yes)
                {
                    List<string> collectItems = new List<string>();
                    foreach (object o in lbx_FilesCollided.Items)
                    {
                        collectItems.Add(o.ToString());
                    }
                    foreach (string o in collectItems)
                    {
                        if (File.Exists(o.ToString()))
                        {
                            try
                            {
                                string filetoDelete = o.ToString();
                                File.Delete(filetoDelete);
                                this.SetStatusBar("Deleted file " + filetoDelete);
                                lbx_FilesCollided.Items.Remove(filetoDelete);
                                if (lbx_FilesCollided.Items.Count == 0)
                                {
                                    //string hashretrieval = DupeLink[filetoDelete][0].Hash;
                                    //string fullnameget = FirstRuns[hashretrieval].fInfo.FullName;
                                    string CollisiontoResolve = DupeLink[filetoDelete];
                                    lbx_FileCollisions.Items.Remove(CollisiontoResolve);
                                    Dupes[DupeLink[filetoDelete]].Clear();
                                }//if (lbx_FilesCollided.Items.Count == 0)
                            }//try
                            catch (Exception ex)
                            {
                                this.ErrorMessageAdd("Unable to delete file. Reason: " + ex.Message);
                            }//catch (Exceptoin ex)
                        }
                    }
                }
            }
            else
            {
                if (lbx_FileCollisions.SelectedItems.Count > 1)
                {
                    if (MessageBox.Show("Do you really want to delete dupes for the selected items?", "Confirm Deletion", MessageBoxButtons.YesNo) == DialogResult.Yes)
                    {
                        List<string> SelectedItems = new List<string>();
                        foreach (object o in lbx_FileCollisions.SelectedItems)
                        {
                            SelectedItems.Add(o.ToString());
                        }
                        foreach (string s in SelectedItems)
                        {

                            List<string> collectItems = new List<string>();

                            if (Dupes.Keys.Contains(s))
                            {
                                foreach (DupeCheck dCheck in Dupes[s])
                                {
                                    collectItems.Add(dCheck.fInfo.FullName);
                                }
                            }

                            foreach (string o in collectItems)
                            {
                                if (File.Exists(o.ToString()))
                                {
                                    try
                                    {
                                        string filetoDelete = o.ToString();
                                        File.Delete(filetoDelete);
                                        this.SetStatusBar("Deleted file " + filetoDelete);
                                        lbx_FilesCollided.Items.Remove(filetoDelete);
                                        if (lbx_FilesCollided.Items.Count == 0)
                                        {
                                            //string hashretrieval = DupeLink[filetoDelete][0].Hash;
                                            //string fullnameget = FirstRuns[hashretrieval].fInfo.FullName;
                                            string CollisiontoResolve = DupeLink[filetoDelete];
                                            lbx_FileCollisions.Items.Remove(CollisiontoResolve);
                                            Dupes[DupeLink[filetoDelete]].Clear();
                                        }//if (lbx_FilesCollided.Items.Count == 0)
                                    }//try
                                    catch (Exception ex)
                                    {
                                        this.ErrorMessageAdd("Unable to delete file. Reason: " + ex.Message);
                                    }//catch (Exceptoin ex)
                                }
                            }
                        }
                    }
                }
                else
                {
                    MessageBox.Show("Unable to delete all duplicates for item. No item is selected.");
                }
            }
        }
        #endregion
        #region "Left-Pane Context Strip Opening"
        private void contextMenuStrip2_Opening(object sender, CancelEventArgs e)
        {
            if (lbx_FileCollisions.Items.Count > 0)
            {
                if (lbx_FileCollisions.SelectedItems.Count == 1)
                {
                    if (!this.isscanning)
                        deleteEveryDupeInListToolStripMenuItem.Enabled = true;
                    else
                        deleteEveryDupeInListToolStripMenuItem.Enabled = false;
                    deleteAllDupesToolStripMenuItem.Enabled = true;
                }
                else
                {
                    if (!this.isscanning)
                        deleteEveryDupeInListToolStripMenuItem.Enabled = true;
                    else
                        deleteEveryDupeInListToolStripMenuItem.Enabled = false;
                    deleteAllDupesToolStripMenuItem.Enabled = true;
                    openFileLocationToolStripMenuItem1.Enabled = false;
                }
            }
            else
            {
                deleteEveryDupeInListToolStripMenuItem.Enabled = false;
                deleteAllDupesToolStripMenuItem.Enabled = false;
            }
        }
        #endregion
        #region "Delete all items lefthand context menu function"
        private void deleteEveryDupeInListToolStripMenuItem_Click(object sender, EventArgs e)
        {
            List<string> Collisions = new List<string>();
            List<string> collectItems = new List<string>();
            if (MessageBox.Show("Do you really want to delete all duplicate items? Some games and other program files may have intentional dupes. Please be sure you want to remove all of the files.", "Confirm Deletion", MessageBoxButtons.YesNo) == DialogResult.Yes)
            {
                foreach (object o in lbx_FileCollisions.Items)
                {
                    Collisions.Add(o.ToString());
                }
                foreach (string s in Collisions)
                {
                    if (Dupes.Keys.Contains(s))
                    {
                        foreach (DupeCheck dCheck in Dupes[s])
                        {
                            collectItems.Add(dCheck.fInfo.FullName);
                        }//foreach (DupeCheck dCheck in Dupes[s])
                    }//if (Dupes.Keys.Contains(s))

                    foreach (string o in collectItems)
                    {
                        if (File.Exists(o.ToString()))
                        {
                            try
                            {
                                string filetoDelete = o.ToString();
                                File.Delete(filetoDelete);
                                this.SetStatusBar("Deleted file " + filetoDelete);
                                lbx_FilesCollided.Items.Remove(filetoDelete);
                                if (lbx_FilesCollided.Items.Count == 0)
                                {
                                    //string hashretrieval = DupeLink[filetoDelete][0].Hash;
                                    //string fullnameget = FirstRuns[hashretrieval].fInfo.FullName;
                                    string CollisiontoResolve = DupeLink[filetoDelete];
                                    lbx_FileCollisions.Items.Remove(CollisiontoResolve);
                                    Dupes[CollisiontoResolve].Clear();
                                }
                            }//try
                            catch (Exception ex)
                            {
                                this.SetStatusBar("Unable to delete file. Reason: " + ex.Message);
                            }//catch (Exception ex)
                        }//if (File.Exists(o.ToString()))
                    }//foreach (string o in collectItems)
                }//foreach (string s in Collisions)
            }//if (MessageBox.Show("Do you really want to delete all duplicate items?", "Confirm Deletion", MessageBoxButtons.YesNo) == DialogResult.Yes)
        }
        #endregion      
        #endregion
        #region "Donate window"
        private void supportBFMViaDonationsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            System.Diagnostics.Process.Start("https://sourceforge.net/donate/index.php?group_id=334540");
        }
        #endregion
        #region "Error Logger"
        private void btn_Clear_Log_Click(object sender, EventArgs e)
        {
            lbx_Error_Log.Items.Clear();
            tab_Errorlog.Text = "Error Log";
        }//private void deleteEveryDupeInListToolStripMenuItem_Click(object sender, EventArgs e)
        private void ErrorMessageAdd(string text)
        {
            if (lbx_Error_Log.InvokeRequired)
            {
                TextMessageCallback d = new TextMessageCallback(ErrorMessageAdd);
                lbx_Error_Log.Invoke(d, new object[] { text });
            }
            else
            {
                lbx_Error_Log.Items.Add(text);
                tab_Errorlog.Text = "Error log (" + lbx_Error_Log.Items.Count + ")";
            }
        }
        #endregion

        private void bulkFileManagerDocumentationToolStripMenuItem_Click(object sender, EventArgs e)
        {
            System.Diagnostics.Process.Start("http://bulkfilemanager.codeplex.com/documentation");
        }

        private void mITLicenseToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MessageBox.Show("Copyright (c) 2010 Stauken" + "\r\n\r\n"
                        + "Permission is hereby granted, free of charge, to any person obtaining a " + "\r\n" 
                        + "copy of this software and associated documentation files (the \"Software\"), " + "\r\n" 
                        + "to deal in the Software without restriction, including without limitation the" + "\r\n"
                        + "rights to use, copy, modify, merge, publish, distribute, sublicense, and/or " + "\r\n" 
                        + "sell copies of the Software, and to permit persons to whom the Software is " + "\r\n" 
                        + "furnished to do so, subject to the following conditions:" + "\r\n\r\n"
                        + "The above copyright notice and this permission notice shall be" + "\r\n" 
                        + "included in all copies or substantial portions of the Software." + "\r\n\r\n"
                        + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,"
                        + "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR " 
                        + "A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT " 
                        + "HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF " 
                        + "CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE " 
                        + "OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.");
        }
        #region "Bulk move"
        // button to get directory to move from in bulk mover
        private void button7_Click(object sender, EventArgs e)
        {
            string dirMoveFrom = BulkMove_tbx_Move_From.Text;
            if (Directory.Exists(dirMoveFrom))
                folderBrowserDialog1.SelectedPath = dirMoveFrom;
            if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
            {
                BulkMove_tbx_Move_From.Text = folderBrowserDialog1.SelectedPath;
            }
        }
        // button to get directory to move to in bulk mover
        private void button8_Click(object sender, EventArgs e)
        {
            string dirMoveTo = BulkMove_tbx_Dir_Move_to.Text;
            if (Directory.Exists(dirMoveTo))
                folderBrowserDialog1.SelectedPath = dirMoveTo;
            if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
            {
                BulkMove_tbx_Dir_Move_to.Text = folderBrowserDialog1.SelectedPath;
            }
        }

        private void BulkMove_btn_Go_Click(object sender, EventArgs e)
        {
            BulkMove_Option_MoveAll = false;
            if (BulkMove_tbx_Dir_Move_to.Text != "" || BulkMove_tbx_Move_From.Text != "")
            {
                if (!BulkMove_cbx_MoveIf_Extension.Checked && !BulkMove_cbx_MoveIf_Filename.Checked && !BulkMove_cbx_MoveIf_DirectoryName.Checked && !BulkMove_cbx_MoveIf_RegEx.Checked)
                {
                    if (MessageBox.Show("This will move all files from all subdirectories of " + BulkMove_tbx_Move_From.Text + " to " + BulkMove_tbx_Dir_Move_to.Text + ". Is that ok?", "Move all?", MessageBoxButtons.OKCancel) == DialogResult.OK)
                    {
                        BulkMove_Option_MoveAll = true;
                        workMove = new System.Threading.Thread(new System.Threading.ThreadStart(BulkMove_doWork));
                        workMove.Start();                        
                    }
                }
                else
                {
                    workMove = new System.Threading.Thread(new System.Threading.ThreadStart(BulkMove_doWork));
                    workMove.Start();
                }
            }
            else
            {
                MessageBox.Show("Please select directories for this process to use.");
            }
        }
        public void BulkMove_doWork()
        {
            this.SetStatusBar("Moves in progress.");
            this.BulkMoveButtonEnable(false);
            if (Directory.Exists(BulkMove_tbx_Move_From.Text))
                this.BulkMove_ParseDir(new DirectoryInfo(BulkMove_tbx_Move_From.Text));
            else
                this.ErrorMessageAdd("Invalid directory in move attempt.");
            this.BulkMoveButtonEnable(true);
            this.SetStatusBar("Moves complete.");
        }
        public void BulkMoveButtonEnable(bool enabled)
        {
            if (BulkMove_btn_Go.InvokeRequired)
            {
                ButtonEnableCallback btnCallback = new ButtonEnableCallback(BulkMoveButtonEnable);
                BulkMove_btn_Go.Invoke(btnCallback, new object[] { enabled });
            }
            else
            {
                BulkMove_btn_Go.Enabled = enabled;
            }
        }
        /// <summary>
        /// Bulk Move parser, much like the other parsers but just moves the files to a central location instead of changing its name.
        /// </summary>
        /// <param name="dir"></param>
        public void BulkMove_ParseDir(DirectoryInfo dir)
        {
            if (Directory.Exists(BulkMove_tbx_Dir_Move_to.Text))
            {
                foreach (FileInfo fInfo in dir.GetFiles())
                {
                    try
                    {
                        if (BulkMove_Option_MoveAll == false)
                        {
                            if (BulkMove_cbx_MoveIf_Extension.Checked)
                                if (fInfo.Extension.Contains(BulkMove_tbx_MoveIf_Extension.Text))
                                    File.Move(fInfo.FullName, BulkMove_tbx_Dir_Move_to.Text + "\\" + fInfo.Name);
                            if (BulkMove_cbx_MoveIf_Filename.Checked)
                                if (fInfo.Name.Contains(BulkMove_tbx_MoveIf_Filename.Text))
                                    File.Move(fInfo.FullName, BulkMove_tbx_Dir_Move_to.Text + "\\" + fInfo.Name);
                            if (BulkMove_cbx_MoveIf_DirectoryName.Checked)
                                if (fInfo.DirectoryName.Contains(BulkMove_tbx_MoveIf_DirectoryName.Text))
                                    File.Move(fInfo.FullName, BulkMove_tbx_Dir_Move_to.Text + "\\" + fInfo.Name);
                            if (BulkMove_cbx_MoveIf_RegEx.Checked)
                                if (System.Text.RegularExpressions.Regex.IsMatch(fInfo.FullName, BulkMove_tbx_MoveIf_RegEx.Text))
                                    File.Move(fInfo.FullName, BulkMove_tbx_Dir_Move_to.Text + "\\" + fInfo.Name);
                        }
                        else
                        {
                            File.Move(fInfo.FullName, BulkMove_tbx_Dir_Move_to.Text + "\\" + fInfo.Name);
                        }
                    }
                    catch (Exception ex)
                    {
                        this.ErrorMessageAdd("Unable to move file " + fInfo.Name + " from " + fInfo.DirectoryName + " due to exception: " + ex.Message);
                    }

                }
                if (!BulkMove_cbx_Options_DoNotRecurseSubfolders.Checked)
                {
                    foreach (DirectoryInfo dInfo in dir.GetDirectories())
                    {
                        try
                        {
                            this.BulkMove_ParseDir(dInfo);
                        }
                        catch (Exception ex)
                        {
                            this.ErrorMessageAdd("Unable to parse directory " + dInfo.FullName + " due to exception: " + ex.Message);
                        }
                    }
                }
                if (dir.GetFiles().Length == 0 && dir.GetDirectories().Length == 0 && BulkMove_cbx_Options_Delete_Empty_Dirs.Checked)
                {
                    dir.Delete();
                }
            }
            else
            {
                this.SetStatusBar("Unable to process move. The directory to move things to does not exist.");
            }
        }
        public bool BulkMove_Option_MoveAll = false;
        #endregion

        #region "ZByte/Empty Dir Area"
        private void contextMenuStrip3_Opening(object sender, CancelEventArgs e)
        {
            if (lbx_ZB_Results.SelectedItems.Count == 1)
            {
                ZeroByte_Delete_All_Items.Enabled = true;
                ZeroByte_Delete_Selected_Items.Enabled = true;
                ZeroByte_Open_File_Location.Enabled = true;
            }
            else if (lbx_ZB_Results.SelectedItems.Count > 1)
            {
                ZeroByte_Delete_Selected_Items.Enabled = true;
                ZeroByte_Open_File_Location.Enabled = false;
                ZeroByte_Delete_All_Items.Enabled = true;
            }
            else if (lbx_ZB_Results.Items.Count > 0)
            {
                ZeroByte_Delete_Selected_Items.Enabled = false;
                ZeroByte_Open_File_Location.Enabled = false;
                ZeroByte_Delete_All_Items.Enabled = true;
            }
            else if (lbx_ZB_Results.Items.Count == 0)
            {
                ZeroByte_Delete_Selected_Items.Enabled = false;
                ZeroByte_Open_File_Location.Enabled = false;
                ZeroByte_Delete_All_Items.Enabled = false;
            }
        }        

        private void btn_Select_ZB_EF_Folder_Click(object sender, EventArgs e)
        {
            string ZBFolder = tbx_ZeroByte_EmptyDir_ToScan.Text;
            if (Directory.Exists(ZBFolder))
                folderBrowserDialog1.SelectedPath = ZBFolder;
            if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
            {
                tbx_ZeroByte_EmptyDir_ToScan.Text = folderBrowserDialog1.SelectedPath;                
            }
        }
        private void ZeroByteAdd(string texttoadd)
        {
            if (lbx_ZB_Results.InvokeRequired)
            {
                TextMessageCallback zblbx = new TextMessageCallback(ZeroByteAdd);
                lbx_ZB_Results.Invoke(zblbx, new object[] { texttoadd });
            }
            else
            {
                lbx_ZB_Results.Items.Add(texttoadd);
            }
        }
        private void ZeroByteButtonEnable(bool enabled)
        {
            if (button9.InvokeRequired)
            {
                ButtonEnableCallback zbbtn = new ButtonEnableCallback(ZeroByteButtonEnable);
                button9.Invoke(zbbtn, new object[] { enabled });
            }
            else
            {
                button9.Enabled = enabled;
            }
        }
        private void ZeroByteScanThread()
        {
            ZeroByteListboxclear();
            this.SetStatusBar("Zero Byte/Empty Dir scan in progress.");
            this.ZeroByteButtonEnable(false);
            this.ZeroByteScan(new DirectoryInfo(tbx_ZeroByte_EmptyDir_ToScan.Text));
            this.ZeroByteButtonEnable(true);
            this.SetStatusBar("Zero Byte/Empty Dir scan complete.");
        }
        private void ZeroByteListboxclear()
        {
            if (lbx_ZB_Results.InvokeRequired)
            {
                ListBoxClearCallback lbClear = new ListBoxClearCallback(ZeroByteListboxclear);
                lbx_ZB_Results.Invoke(lbClear);
            }
            else
            {
                this.lbx_ZB_Results.Items.Clear();
            }
        }
        // Check if the files meet the criteria
        private void ZeroByteScan(DirectoryInfo dir)
        {
            try
            {
                if (!this.zbScanStop)
                {
                    foreach (FileInfo fInfo in dir.GetFiles())
                    {
                        if (fInfo.Length == 0 && cbx_ZeroByte.Checked)
                            ZeroByteAdd(fInfo.FullName);
                    }
                    foreach (DirectoryInfo dInfo in dir.GetDirectories())
                    {
                        ZeroByteScan(dInfo);
                    }
                    if (dir.GetFiles().Length == 0 && dir.GetDirectories().Length == 0 && cbx_Empty_Dirs.Checked)
                    {
                        ZeroByteAdd(dir.FullName);
                    }
                }
                else
                {
                    this.SetStatusBar("Exiting ZeroByte/Empty Directory scan.");
                }
            }
            catch (Exception ex)
            {
                this.ErrorMessageAdd("Unable to parse Directory or file. Reason: " + ex.Message);
            }
        }

        // Zero Byte file/Empty Dir checker
        private void button9_Click(object sender, EventArgs e)
        {
            if (tbx_ZeroByte_EmptyDir_ToScan.Text != "")
            {
                // If one of the options are selected we're ok here.
                if (this.cbx_ZeroByte.Checked || this.cbx_Empty_Dirs.Checked)
                {                    
                    zbScanStop = false;
                    workZbscan = new System.Threading.Thread(new System.Threading.ThreadStart(ZeroByteScanThread));
                    workZbscan.Start();
                    btn_StopZbScan.Enabled = true;
                }
                else
                {
                    MessageBox.Show("Please select a item to scan for.");
                }
            }
            else
            {
                MessageBox.Show(Form1.NODIRECTORYERROR);
            }
        }
        private void ZeroByte_Delete_All_Items_Click(object sender, EventArgs e)
        {
            List<string> ZeroByteResults = new List<string>();
            foreach (object o in lbx_ZB_Results.Items)
            {
                ZeroByteResults.Add(o.ToString());
            }
            foreach (string s in ZeroByteResults)
            {
                this.DeleteItem(s);
                lbx_ZB_Results.Items.Remove(s);
            }
        }
        public void DeleteItem(string s)
        {
            if (File.Exists(s))
            {
                try
                {
                    File.Delete(s);
                }
                catch (Exception ex)
                {
                    this.ErrorMessageAdd("Unable to delete file: " + ex.Message);
                }
            }
            else
            {
                if (Directory.Exists(s))
                {
                    try
                    {
                        Directory.Delete(s);
                    }
                    catch (Exception ex)
                    {
                        this.ErrorMessageAdd("Unable to delete directory: " + ex.Message);
                    }
                }
                else
                {
                    this.ErrorMessageAdd("Unable to handle list item " + s);
                }
            }
        }
        private void ZeroByte_Delete_Selected_Items_Click(object sender, EventArgs e)
        {
            List<string> ZeroByteSelections = new List<string>();
            foreach (object o in lbx_ZB_Results.SelectedItems)
            {
                ZeroByteSelections.Add(o.ToString());
            }
            foreach (string s in ZeroByteSelections)
            {
                this.DeleteItem(s);
                lbx_ZB_Results.Items.Remove(s);
            }
        }
        private void ZeroByte_Open_File_Location_Click(object sender, EventArgs e)
        {
            System.Diagnostics.Process.Start("explorer.exe", lbx_ZB_Results.SelectedItem.ToString());
        }
        private void btn_StopZbScan_Click(object sender, EventArgs e)
        {
            this.zbScanStop = true;
            btn_StopZbScan.Enabled = false;
        }
        private bool zbScanStop = false;
        #endregion

        #region "Shredder Logic"
        private string ChunkData(int SizeOf)
        {
            string OnesorZeroes = "01";
            string OtherVals = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-=!@#$%^&*()_+[]{};':\",./<>?";
            StringBuilder retval = new StringBuilder();
            Random randGen = new Random(new Random().Next());
            for (int i = 0; i < SizeOf; i++)
            {                
                if (radio_Use1sand0s.Checked)
                    retval.Append(randGen.Next() % 2 == 0 ? OnesorZeroes[0] : OnesorZeroes[1]);
                else
                {
                    retval.Append(OtherVals[(int)(randGen.NextDouble() * OtherVals.Length)]);
                }
            }
            return retval.ToString();
        }
        private void WipeFile(string filename, int count)
        {
            this.SetStatusBar("Wiping file..");
            if (File.Exists(filename))
            {
                FileInfo fInfo = new FileInfo(filename);
                for (int i = 1; i < count; i++)
                {
                    if (!this.stopshredding)
                    {
                        this.ControlShredFileLabel(fInfo.Name);
                        this.ControlShredFolderLabel(fInfo.DirectoryName);
                        this.ControlPassLabel(i.ToString() + " out of " + count.ToString());
                        FileStream fStream = File.Open(filename, FileMode.Open);
                        // Get the length of the file.
                        long streamlength = fStream.Length;
                        // Get rid of the stream used to open the file.
                        fStream.Close();
                        fStream.Dispose();
                        // Random generator for fluff data.
                        Random randgen = new Random();
                        // Truncate the file so that our position is 1.
                        fStream = File.Open(filename, FileMode.Open);
                        // Set up a writer
                        fStream.Lock(0, streamlength);
                        StreamWriter sWrite = new StreamWriter(fStream);
                        sWrite.AutoFlush = true;
                        sWrite.BaseStream.Position = 0;
                        // Run through the file and chunk out the data.
                        int interval = 1024;
                        while (sWrite.BaseStream.Position < streamlength)
                        {
                            this.ControlByteCount(sWrite.BaseStream.Position.ToString() + " / " + streamlength);
                            if (interval + sWrite.BaseStream.Position < streamlength)
                                sWrite.Write(ChunkData(interval));
                            else
                            {
                                sWrite.Write(ChunkData(Convert.ToInt32(streamlength - sWrite.BaseStream.Position)));
                            }
                        }
                        sWrite.Flush();
                        fStream.Flush();
                        fStream.Unlock(0, streamlength);
                        this.ControlPassLabel("File wiped.");
                        this.ControlByteCount("N/A");
                        sWrite.Close();
                        fStream.Close();
                        sWrite.Dispose();
                        fStream.Dispose();
                    }
                    else
                        this.SetStatusBar("Stopping shred.");
                }
                try
                {
                    if (!this.stopshredding)
                    {
                        File.Delete(filename);
                        this.SetStatusBar("Wiped file.");
                    }
                }
                catch (Exception ex)
                {                    
                    this.ErrorMessageAdd("Exception while wiping file: " + ex.Message);
                }
            }
        }
        public void RecurseShred(DirectoryInfo dInfo)
        {
            if (!this.stopshredding)
            {
                foreach (FileInfo fInfo in dInfo.GetFiles())
                {
                    if(!this.stopshredding)
                        this.WipeFile(fInfo.FullName, Convert.ToInt32(numericOverwriteCount.Value));
                }
                foreach (DirectoryInfo dirs in dInfo.GetDirectories())
                {
                    if(!this.stopshredding)
                        this.RecurseShred(dirs);
                }
                if (cbx_Shred_Delete_Empty_dirs.Checked)
                {
                    if (!this.stopshredding)
                    {
                        if (dInfo.GetFiles().Length == 0 && dInfo.GetDirectories().Length == 0)
                        {
                            try
                            {
                                dInfo.Delete();
                            }
                            catch (Exception ex)
                            {
                                this.ErrorMessageAdd("Cannot delete presumably empty directory " + dInfo.FullName + ", reason: " + ex.Message);
                            }
                        }
                    }
                }
            }
        }
        public void WorkShred()
        {
            if (this.radio_Shred_Folder.Checked)
            {
                if (Directory.Exists(tbx_Folder_to_Shred.Text))
                {
                    this.ControlShredButton(false);
                    this.SetStatusBar("Shredding " + tbx_Folder_to_Shred.Text);
                    this.RecurseShred(new DirectoryInfo(tbx_Folder_to_Shred.Text));
                    this.SetStatusBar("Finished shredding " + tbx_Folder_to_Shred.Text);
                    this.ControlShredButton(true);
                }//if (Directory.Exists(tbx_Folder_to_Shred.Text))
                else
                {
                    this.ErrorMessageAdd("Directory " + tbx_Folder_to_Shred.Text + " does not exist.");
                }//else
            }//if (this.radio_Shred_Folder.Checked)
            else
            {
                if (File.Exists(tbx_File_to_Shred.Text))
                {
                    this.ControlShredButton(false);
                    this.SetStatusBar("Shredding " + tbx_File_to_Shred.Text);
                    this.WipeFile(tbx_File_to_Shred.Text, Convert.ToInt32(numericOverwriteCount.Value));                    
                    this.SetStatusBar("Finished shredding " + tbx_File_to_Shred.Text);
                    this.ControlShredButton(true);
                }//if (File.Exists(tbx_File_to_Shred.Text))
                else
                {
                    this.ErrorMessageAdd("File " + tbx_File_to_Shred.Text + " does not exist.");
                }
            }//else
        }
        private void btn_ShredGo_Click(object sender, EventArgs e)
        {
            this.stopshredding = false;
            this.workShred = new System.Threading.Thread(new System.Threading.ThreadStart(WorkShred));
            if (trackBar1.Value == 0)
                this.workShred.Priority = ThreadPriority.Lowest;
            if (trackBar1.Value == 1)
                this.workShred.Priority = ThreadPriority.BelowNormal;
            if (trackBar1.Value == 2)
                this.workShred.Priority = ThreadPriority.Normal;
            this.workShred.Start();
            this.btn_Stop_Shredding.Enabled = true;
        }
        /// <summary>
        /// Thread-safe access of the button that starts the wipe process.
        /// </summary>
        /// <param name="Enabled">Whether the button is enabled or disabled.</param>
        public void ControlShredButton(bool Enabled)
        {
            if (this.btn_ShredGo.InvokeRequired)
            {
                ButtonEnableCallback d = new ButtonEnableCallback(ControlShredButton);
                this.btn_ShredGo.Invoke(d, new object[] { Enabled });
            }
            else
            {
                this.btn_ShredGo.Enabled = Enabled;
            }
        }
        public void ControlShredFileLabel(string text)
        {
            if (label_Shredding_File.InvokeRequired)
            {
                TextMessageCallback d = new TextMessageCallback(ControlShredFileLabel);
                this.label_Shredding_File.Invoke(d, new object[] { text });
            }
            else
            {
                label_Shredding_File.Text = text;
            }
        }
        public void ControlShredFolderLabel(string text)
        {
            if (label_Shredding_Folder.InvokeRequired)
            {
                TextMessageCallback d = new TextMessageCallback(ControlShredFolderLabel);
                this.label_Shredding_Folder.Invoke(d, new object[] { text });
            }
            else
            {
                label_Shredding_Folder.Text = text;
            }
        }
        public void ControlPassLabel(string text)
        {
            if (lbl_Shred_Passcount.InvokeRequired)
            {
                TextMessageCallback d = new TextMessageCallback(ControlPassLabel);
                this.lbl_Shred_Passcount.Invoke(d, new object[] { text });
            }
            else
            {
                lbl_Shred_Passcount.Text = text;
            }
        }
        public void ControlByteCount(string text)
        {
            if (lbl_Shredder_Byte_Count.InvokeRequired)
            {
                TextMessageCallback d = new TextMessageCallback(ControlByteCount);
                this.lbl_Shredder_Byte_Count.Invoke(d, new object[] { text });
            }
            else
            {
                this.lbl_Shredder_Byte_Count.Text = text;
            }
        }
        private void btn_Shred_File_Select_Click(object sender, EventArgs e)
        {
            if (openFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                tbx_File_to_Shred.Text = openFileDialog1.FileName;
            }
        }
        private void btn_Select_Folder_Click(object sender, EventArgs e)
        {
            string selectedPath = tbx_Folder_to_Shred.Text;
            if (Directory.Exists(selectedPath))
                folderBrowserDialog1.SelectedPath = selectedPath;
            if (folderBrowserDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                tbx_Folder_to_Shred.Text = folderBrowserDialog1.SelectedPath;
            }
        }
        private void btn_Stop_Shredding_Click(object sender, EventArgs e)
        {
            stopshredding = true;
            this.btn_Stop_Shredding.Enabled = false;
        }
        #endregion      


        private void selectjoin_btn_Add_Files_Click(object sender, EventArgs e)
        {
            if (openFileDialog2.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                foreach (string s in openFileDialog2.FileNames)
                {
                    this.splitjoin_ListBox_FileSelect.Items.Add(s);
                }
            }
        }

        private void button10_Click(object sender, EventArgs e)
        {
            splitjoin_ListBox_FileSelect.Items.Clear();
        }

        private void splitjoin_radio_Split_CheckedChanged(object sender, EventArgs e)
        {
            groupBox16.Visible = true;
        }
        private void EnableSplitButton(bool enabled)
        {
            if (button11.InvokeRequired)
            {
                ButtonEnableCallback d = new ButtonEnableCallback(EnableSplitButton);
                this.button11.Invoke(d, new object[] { enabled });                
            }
            else
            {
                button11.Enabled = enabled;
            }            
        }
        private void splitjoin_Select_Output_Click(object sender, EventArgs e)
        {
            if (splitjoin_radio_Split.Checked)
            {
                if (folderBrowserDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
                    splitjoin_tbx_Output.Text = folderBrowserDialog1.SelectedPath;
                }
            }
            else
                if (splitjoin_radio_Join.Checked)
                {
                    if (saveFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                    {
                        splitjoin_tbx_Output.Text = saveFileDialog1.FileName;
                    }
                }
        }
        private void DoSplit()
        {
            ulong getSizeValue = 1024;
            if (splitjoin_Radio_Floppy_144mb.Checked)
                getSizeValue = 1440000;
            if (splitjoin_radio_GB_Select.Checked)
                getSizeValue = Convert.ToUInt64(splitjoin_numeric_GB_Select.Value * (1024 * 1024 * 1024));
            if (splitjoin_radio_Numeric_MB_Select.Checked)
                getSizeValue = Convert.ToUInt64(splitjoin_Numeric_MB_Selection.Value * (1024 * 1024));
            if (splitjoin_radio_KB_Select.Checked)
                getSizeValue = Convert.ToUInt64(splitjoin_numeric_KB_Select.Value * 1024);
            if (splitjoin_radio_Size_Split_650.Checked)
                getSizeValue = 650000000;
            if (splitjoin_radio_size_DVD.Checked)
                getSizeValue = 4700000000;
            if (Directory.Exists(splitjoin_tbx_Output.Text))
            {
                foreach (object li in splitjoin_ListBox_FileSelect.Items)
                {
                    uint fileincrementor = 0;
                    uint incrementsstopat = 0;
                    if (File.Exists(li.ToString()))
                    {
                        FileInfo fInfo = new FileInfo(li.ToString());
                        DirectoryInfo dInfo = new DirectoryInfo(splitjoin_tbx_Output.Text);
                        FileStream fOpen = File.Open(li.ToString(), FileMode.Open);
                        BinaryReader fRead = new BinaryReader(fOpen,Encoding.Unicode);
                        StreamReader sReader = new StreamReader(fOpen, true);
                        string encoderName = "";
                        if (sReader.CurrentEncoding.EncodingName != "")
                        {
                            encoderName = sReader.CurrentEncoding.EncodingName;
                        }
                        FileStream getfile = null;
                        StreamWriter sWrite = null;
                        incrementsstopat = Convert.ToUInt32((fRead.BaseStream.Length / Convert.ToInt64(getSizeValue)));
                        while (fileincrementor <= incrementsstopat)
                        {
                            string append = "";
                            if (splitjoin_cbx_prefix_with_part.Checked)
                                append += ".part";
                            append += "." + fileincrementor.ToString("000");
                            
                            try
                            {
                                getfile = File.Create(dInfo.FullName + "\\" + fInfo.Name + append);
                            }
                            catch (Exception ex)
                            {
                                this.ErrorMessageAdd("Error while splitting file: " + ex.Message);
                            }
                            if (getfile != null)
                            {
                                sWrite = new StreamWriter(getfile);
                            }                                    
                            int interval = 1024;
                            if (fOpen.Length > (1024 * 1024))
                                interval = 1024 * 1024;
                            else if (fOpen.Length > (1024 * 256))
                                interval = 1024 * 256;
                            byte[] buffer = new byte[interval];
                            if (sWrite != null)
                            {
                                long curmax = (Convert.ToInt64(getSizeValue) * (fileincrementor + 1));
                                if (curmax > fOpen.Length)
                                    curmax = fOpen.Length;
                                while (fRead.BaseStream.Position < curmax)
                                {                                       
                                    if (interval + fOpen.Position < curmax)
                                    {
                                        fOpen.Read(buffer,0,interval);
                                        getfile.Write(buffer,0,interval);
                                    }//if (interval + fRead.BaseStream.Position < Convert.ToInt64(getSizeValue))
                                    else
                                    {
                                        if (fOpen.Position < curmax)
                                        {
                                            long diff = curmax - fRead.BaseStream.Position;
                                            byte[] tempbuff = new byte[diff];
                                            fOpen.Read(tempbuff, 0, Convert.ToInt32(diff));
                                            getfile.Write(tempbuff, 0, Convert.ToInt32(diff));
                                        }
                                        sWrite.Close();
                                        sWrite.Dispose();
                                        getfile.Close();
                                        getfile.Dispose();                                        
                                    }//else
                                }//while (fRead.BaseStream.Position < Convert.ToInt64(getSizeValue))
                            }//if (sWrite != null)
                            else
                            {
                                this.ErrorMessageAdd("Error in splitting file: Unable to write output part file, StreamWriter is null.");
                            }//else
                            this.SetStatusBar("Created part " + fileincrementor.ToString());
                            fileincrementor++;
                        }//while (fileincrementor < incrementsstopat)
                        fRead.Close();                        
                        this.SetStatusBar("Completed splitting file " + li.ToString());
                    }//if (File.Exists(li.ToString()))
                }//foreach (object li in splitjoin_ListBox_FileSelect.Items)
            }//if (Directory.Exists(splitjoin_tbx_Output.Text))
            else
            {
                this.ErrorMessageAdd("Output directory does not exist for split command.");
            }//else
            this.SetStatusBar("Split job complete.");
            EnableSplitButton(true);
        }
        private void DoJoin()
        {
            try
            {
                this.SetStatusBar("Joining files..");
                FileStream outputFile = new FileStream(splitjoin_tbx_Output.Text,FileMode.OpenOrCreate,FileAccess.Write);                
                FileStream partReader = null;
                foreach (object li in splitjoin_ListBox_FileSelect.Items)
                {
                    this.SetStatusBar("Merging " + li.ToString());
                    if(File.Exists(li.ToString()))
                    {
                        partReader = new FileStream(li.ToString(),FileMode.Open,FileAccess.Read);
                        byte[] buff = new byte[1024];
                        while (partReader.Position < partReader.Length )
                        {
                            if (partReader.Position + 1024 < partReader.Length)
                            {
                                partReader.Read(buff, 0, 1024);
                                outputFile.Write(buff, 0, 1024);
                            }
                            else
                            {
                                int diff = Convert.ToInt32(partReader.Length - partReader.Position);
                                byte[] diffbuff = new byte[diff];
                                partReader.Read(diffbuff, 0, diff);
                                outputFile.Write(diffbuff, 0, diff);
                            }
                        }                        
                        partReader.Close();
                        partReader.Dispose();
                    }
                    else
                    {
                        this.ErrorMessageAdd("Unable to open file " + li.ToString());
                    }
                }
                outputFile.Close();
                outputFile.Dispose();
                this.SetStatusBar("Completed joining file.");
                EnableSplitButton(true);
            }
            catch (Exception ex)
            {
                this.ErrorMessageAdd("Error while joining file: " + ex.Message);
            }
        }
        private void button11_Click(object sender, EventArgs e)
        {
            if (splitjoin_radio_Split.Checked || splitjoin_radio_Join.Checked)
            {
                if (splitjoin_radio_Split.Checked)
                {
                    if (Directory.Exists(splitjoin_tbx_Output.Text) && splitjoin_ListBox_FileSelect.Items.Count > 0)
                    {                        
                        workSplitjoin = new System.Threading.Thread(new System.Threading.ThreadStart(DoSplit));
                        button11.Enabled = false;
                        workSplitjoin.Start();
                    }
                    else
                    {
                        string msgtoShow = "";
                        if (splitjoin_ListBox_FileSelect.Items.Count == 0)
                            msgtoShow += "You must select file(s) to split.\r\n";
                        if (!Directory.Exists(splitjoin_tbx_Output.Text))
                            msgtoShow += "The selected output directory does not exist.\r\n";
                        MessageBox.Show(msgtoShow);
                    }
                }
                else if(splitjoin_radio_Join.Checked)
                {
                    if (splitjoin_ListBox_FileSelect.Items.Count > 1)
                    {
                        workSplitjoin = new System.Threading.Thread(new System.Threading.ThreadStart(DoJoin));
                        // start thread
                        button11.Enabled = false;
                        workSplitjoin.Start();
                    }
                    else
                    {
                        MessageBox.Show("You must select multiple files to join.");
                    }
                }
            }
        }

        private void splitjoin_radio_Join_CheckedChanged(object sender, EventArgs e)
        {
            groupBox16.Visible = false;
        }
        
    }//public partial class Form1 : Form
}//namespace BulkFileManager
